package com.onpositive.realmview.queryeditor;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import com.onpositive.commons.Activator;
import com.onpositive.queryeditor.storaging.StorageManager;
import com.onpositive.realmview.AbstractRealmView;
import com.onpositive.realmview.IRealmView;
import com.onpositive.realmview.model.GlobalChangeBroadcaster;
import com.onpositive.realmview.model.IHasViews;
import com.onpositive.realmview.model.IModelObject;
import com.onpositive.realmview.model.IProviderWithCustomDefaults;
import com.onpositive.realmview.model.ITemplateCustomizer;
import com.onpositive.realmview.model.IViewConfigurationDescriber;
import com.onpositive.realmview.model.IViewGrouping;
import com.onpositive.realmview.model.IWorkingCopy;
import com.onpositive.realmview.model.filtering.IMayBeFilteredBySearch;
import com.onpositive.realmview.model.realm.IPositionWithTopViews;
import com.onpositive.realmview.model.realm.IRealmPosition;
import com.onpositive.realmview.search.GlobalSearch;
import com.onpositive.semantic.model.api.property.DefaultPropertyMetadata;
import com.onpositive.semantic.model.api.property.IProperty;
import com.onpositive.semantic.model.api.property.IPropertyProvider;
import com.onpositive.semantic.model.api.property.adapters.ITextLabelProvider;
import com.onpositive.semantic.model.core.IKnownsImageObject;
import com.onpositive.semantic.model.realm.AbstractFilter;
import com.onpositive.semantic.model.realm.FilteringRealm;
import com.onpositive.semantic.model.realm.IModifiableRealm;
import com.onpositive.semantic.model.realm.IRealm;
import com.onpositive.semantic.model.realm.IRealmChangeListener;
import com.onpositive.semantic.model.realm .ISetDelta;
import com.onpositive.semantic.model.realm.Realm;

public abstract class ClassificationRealmPosition implements IPositionWithTopViews,
		IKnownsImageObject, ITemplateCustomizer, IMayBeFilteredBySearch {

	protected final class ClassificationFilter extends AbstractFilter {
		public boolean accept(Object element) {
			//SyncingDocument sd = (SyncingDocument) d;
			//SimpleDocument simple = sd.acqureDocument();
			l2: for (Classification c : classification) {
				Object fieldValue = propertyProvider.getProperty(element,c.property);
				if (c.value instanceof NotValue) {
					NotValue m = (NotValue) c.value;
					if (fieldValue instanceof Collection) {
						Collection<?> mq = (Collection<?>) fieldValue;
						for (Object z : mq) {
							if (m.contains(z)) {
								return false;
							}
						}
					}
					if (!m.contains(fieldValue)) {
						continue;
					}
					return false;
				}
				if (fieldValue != null) {
					if (fieldValue instanceof Collection) {
						Collection<?> mq = (Collection<?>) fieldValue;
						for (Object z : mq) {
							if (z.equals(c.value)) {
								continue l2;
							}
						}
					}
					if (fieldValue.equals(c.value)) {
						continue;
					}
					return false;
				} else {
					boolean accept = c.value == null
							|| c.value.toString().length() == 0;
					if (!accept) {
						return false;
					}
				}
			}
			return true;
		}

	}

	public final static class NotValue extends HashSet<Object> {

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public NotValue(Collection m) {
			super(m);
		}

		public String toString() {
			return "Not " + super.toString();
		}

	}

	public static class Classification {
		public final String property;
		public final Object value;

		public Classification(String property, Object value) {
			super();
			this.property = property;
			this.value = value;
		}

		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result
					+ ((property == null) ? 0 : property.hashCode());
			result = prime * result + ((value == null) ? 0 : value.hashCode());
			return result;
		}

		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			Classification other = (Classification) obj;
			if (property == null) {
				if (other.property != null)
					return false;
			} else if (!property.equals(other.property))
				return false;
			if (value == null) {
				if (other.value != null)
					return false;
			} else if (!value.equals(other.value))
				return false;
			return true;
		}
	}

	protected final IHasViews<?> parent;
	protected final Classification[] classification;
	protected IPropertyProvider propertyProvider;
	protected ViewRealm result;
	protected AbstractRealmView re;

	protected FilteringRealm<Object> filteringRealm;

	public ClassificationRealmPosition(IHasViews<?> parent,
			Classification... classification) {
		super();
		this.parent = parent;
		this.classification = classification;

	}

	public IViewConfigurationDescriber getQueryProvider() {
		return parent.getQueryProvider();
	}

	@SuppressWarnings("unchecked")
	public IRealm<Object> getViewRealm() {
		if (filteringRealm != null) {
			return filteringRealm;
		}
		filteringRealm = new FilteringRealm<Object>(
				(IRealm) parent.getViewRealm(), createFilter()) {

			public void dispose() {
				super.dispose();
				filteringRealm = null;
			}

		};
		return filteringRealm;
	}

	protected AbstractFilter createFilter() {
		return new ClassificationFilter();
	}

	public IModifiableRealm<IRealmView> views() {
		if (result != null) {
			return result;
		}
		result = new ViewRealm();
		DataInputStream inputStream = null;
		try {
			inputStream = StorageManager.getInstance().getInputStream(getUrl(),"views");
		} catch (FileNotFoundException e1) {
			// Shouldn't happen, null should be returned instead
			e1.printStackTrace();
		}
		if (inputStream != null)
		{
			try {
				result.load(inputStream, this);
				addViewRealmListener();
				return result;
			} catch (IOException e) {
				addViewRealmListener();
				return result;
			}
		} else {
			createDefaultViews();
		}
		addViewRealmListener();

		return result;
	}

	protected void createDefaultViews() {
		Collection<IRealmView> defaultViews = (Collection<IRealmView>) parent
				.createDefaultViewsFor(this);
		if (defaultViews == null) {
			throw new RuntimeException();
		}
		result.add(defaultViews);

	}

	private void addViewRealmListener() {
		result.addRealmChangeListener(new IRealmChangeListener<IRealmView>() {

			public void realmChanged(IRealm<IRealmView> realmn,
					ISetDelta<IRealmView> delta) {
				try {
					DataOutputStream outputStream = StorageManager.getInstance().getOutputStream(getUrl(),"views");
					if (outputStream == null)
						throw new IOException("Failed to write into " + getUrl());
					result.store(outputStream);
					GlobalChangeBroadcaster
							.notifyChanged(ClassificationRealmPosition.this);
				} catch (IOException e) {
					Activator.log(e);
				}
			}
		});
	}

	public Set<IModelObject> relatedObjects() {
		return new HashSet<IModelObject>(views().getContents());
	}

	public <T> T getAdapter(Class<T> adapter) {
		return parent.getAdapter(adapter);
	}

	public String toString() {
		StringBuilder bld = new StringBuilder();
		for (Classification f : classification) {
			IProperty property = parent.getQueryProvider().provider()
					.getProperty(null, f.property);
			if (property != null) {
				ITextLabelProvider adapter = property.getPropertyMetaData()
						.getAdapter(ITextLabelProvider.class);
				if (adapter != null) {
					bld.append(adapter.getText(f.value));
				} else {
					bld.append(f.value.toString());
				}
				continue;
			}
			bld.append(property.getPropertyMetaData().getName());
			bld.append(':');
			bld.append(f.value);
			bld.append("->");
		}
		if (bld.length() > 0) {
			bld.delete(bld.length() - 2, bld.length());
		}
		return bld.toString() + "(" + parent.toString() + ")";
	}

	/**
	 * Returns human-readable name for object
	 * 
	 * @return human-readable name for object
	 */
	public String getName() {
		if (classification.length == 1) {
			Object value = classification[0].value;
			IViewConfigurationDescriber queryProvider = parent.getQueryProvider();
			if( queryProvider != null){
				IProperty property = queryProvider.provider().getProperty(null, classification[0].property);
				if( property != null){
					ITextLabelProvider tlp = property.getPropertyMetaData().getAdapter(ITextLabelProvider.class);
					if( tlp != null){
						return tlp.getText(value);
					}
				}
			}
			return value.toString();
		}
		return "All";
	}

	public abstract IViewGrouping[] getGoodGroupings(IRealmView p);

	public IRealmPosition derive(IProperty p, Object value) {
		ArrayList<Classification> classify = new ArrayList<Classification>();
		classify.addAll(Arrays.asList(this.classification));
		Classification m = new Classification(p.getId(), value);
		classify.add(m);
		return createClassificationPosition(parent,
				classify.toArray(new Classification[classify.size()]));
	}

	protected abstract IRealmPosition createClassificationPosition(IHasViews<?> parent2,
			Classification[] array);

	public IRealmPosition deriveInverse(IProperty p, Object propertyValue) {
		Collection m = null;
		if (propertyValue instanceof Collection) {
			m = (Collection) propertyValue;
		} else {
			m = Collections.singleton(propertyValue);
		}
		return derive(p, new NotValue(m));
	}

	public Classification[] getClassifications() {
		return this.classification;
	}

	public IHasViews<?> getParent() {
		return parent;
	}

	public abstract IRealmView itemView();

	public IModifiableRealm<IRealmPosition> positions() {
		return new Realm<IRealmPosition>();
	}

	public String getImageID() {
		for (Classification f : classification) {
			IViewConfigurationDescriber queryProvider = parent
					.getQueryProvider();
			IPropertyProvider provider = queryProvider.provider();
			if (provider == null) {
				throw new IllegalArgumentException();
			}
			IProperty property = provider.getProperty(null, f.property);
			if (property != null) {
				DefaultPropertyMetadata m = (DefaultPropertyMetadata) property
						.getPropertyMetaData();
				Object object = m.get(DefaultPropertyMetadata.IMAGE_KEY);
				if (object != null) {
					return object.toString();
				}
			}
		}
		return "com.alpha.rcp.dashboard.product";
	}

	public abstract void load(DataInputStream stream) throws IOException;

	public void store(DataOutputStream stream) throws IOException {

	}

	public String getUrl() {
		String url = parent.getUrl();
		StringBuilder bld = new StringBuilder();
		bld.append(url);
		bld.append('/');
		int a = 0;
		for (Classification f : classification) {
			bld.append(f.property);
			bld.append('=');
			bld.append(f.value);
			if (a != classification.length - 1) {
				bld.append(';');
			}
		}
		return bld.toString();
	}

	public abstract IRealm topViews();

	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + Arrays.hashCode(classification);
		result = prime * result + ((parent == null) ? 0 : parent.hashCode());
		return result;
	}

	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ClassificationRealmPosition other = (ClassificationRealmPosition) obj;
		if (!Arrays.equals(classification, other.classification))
			return false;
		if (parent == null) {
			if (other.parent != null)
				return false;
		} else if (!parent.equals(other.parent))
			return false;
		return true;
	}

	public abstract IWorkingCopy newCopy();
//	{
//		ObjectWorkingCopy workingCopy = new WorkingCopy(null) {
//
//			public IPropertyProvider getPropertyProvider() {
//				return getQueryProvider().provider();
//			}
//
//		};
//		for (Classification m : classification) {
//			if (!(m.value instanceof NotValue)) {
//				workingCopy.setFieldValue(m.property, m.value);
//			}
//		}
//		return workingCopy;
//	}

	public Object getDefaultValue(String propId) {
		IPropertyProvider provider = parent.getQueryProvider().provider();
		IProviderWithCustomDefaults cd = (IProviderWithCustomDefaults) provider;
		return cd.getDefaultFor(propId, getUrl());

	}

	public void setDefaultValue(String propId, Object value) {
		IPropertyProvider provider = parent.getQueryProvider().provider();
		IProviderWithCustomDefaults cd = (IProviderWithCustomDefaults) provider;
		cd.setDefaultFor(propId, getUrl(), value);
	}

	public abstract void customize(Object document);

	public Collection<IRealmView> createDefaultViewsFor(
			IRealmPosition classificationRealmPosition) {
		return parent.createDefaultViewsFor(classificationRealmPosition);
	}

	public Object[] getRealmContents() {
		Object[] m = parent.getRealmContents();
		ArrayList<Object> result = new ArrayList<Object>();
		AbstractFilter createFilter = createFilter();
		for (Object z : m) {
			if (createFilter.accept(z)) {
				result.add(z);
			}
		}
		return result.toArray(new Object[result.size()]);
	}

	public boolean isFiltered() {
		String pattern = GlobalSearch.getPattern();
		if (GlobalSearch.getPattern() != null && pattern.length() > 0) {
			AbstractFilter createFilter = createFilter();
			Object[] l = parent.getRealmContents();
			for (Object a : l) {
				if (createFilter.accept(a)) 
					return true;
			}
			return true;
		}
		return false;
	}

}
